package app.action;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Control;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

import sun.rmi.runtime.Log;

public class BasicPlayer implements Runnable
{
	public static int EXTERNAL_BUFFER_SIZE = 16000;
	public static int SKIP_INACCURACY_SIZE = 1200;
	protected Thread m_thread = null;
	protected Object m_dataSource = null;
	protected AudioInputStream m_encodedaudioInputStream;
	protected int encodedLength = -1;
	protected AudioInputStream m_audioInputStream;
	protected AudioFileFormat m_audioFileFormat;
	protected SourceDataLine m_line;
	protected FloatControl m_gainControl;
	protected FloatControl m_panControl;
	protected String m_mixerName = null;
	private int m_lineCurrentBufferSize = -1;
	private int lineBufferSize = -1;
	private long threadSleep = -1L;
	public static final int UNKNOWN = -1;
	public static final int PLAYING = 0;
	public static final int PAUSED = 1;
	public static final int STOPPED = 2;
	public static final int OPENED = 3;
	public static final int SEEKING = 4;
	private int m_status = -1;
	private Collection m_listeners = null;
	private Map empty_map = new HashMap();

	public BasicPlayer()
	{
		reset();
	}

	protected void reset()
	{
		this.m_status = -1;
		if (this.m_audioInputStream != null)
			synchronized (this.m_audioInputStream)
			{
				closeStream();
			}
		this.m_audioInputStream = null;
		this.m_audioFileFormat = null;
		this.m_encodedaudioInputStream = null;
		this.encodedLength = -1;
		if (this.m_line != null)
		{
			this.m_line.stop();
			this.m_line.close();
			this.m_line = null;
		}
		this.m_gainControl = null;
		this.m_panControl = null;
	}

	public Collection getListeners()
	{
		return this.m_listeners;
	}

	public void setLineBufferSize(int paramInt)
	{
		this.lineBufferSize = paramInt;
	}

	public int getLineBufferSize()
	{
		return this.lineBufferSize;
	}

	public int getLineCurrentBufferSize()
	{
		return this.m_lineCurrentBufferSize;
	}

	public void setSleepTime(long paramLong)
	{
		this.threadSleep = paramLong;
	}

	public long getSleepTime()
	{
		return this.threadSleep;
	}

	public int getStatus()
	{
		return this.m_status;
	}

	public void open(File paramFile)

	{
		if (paramFile != null)
		{
			this.m_dataSource = paramFile;
			initAudioInputStream();
		}
	}

	public void open(URL paramURL)

	{
		if (paramURL != null)
		{
			this.m_dataSource = paramURL;
			initAudioInputStream();
		}
	}

	public void open(InputStream paramInputStream)

	{
		if (paramInputStream != null)
		{
			this.m_dataSource = paramInputStream;
			initAudioInputStream();
		}
	}

	protected void initAudioInputStream()

	{
		try
		{
			reset();
			notifyEvent(0, getEncodedStreamPosition(), -1.0D, this.m_dataSource);
			if ((this.m_dataSource instanceof URL))
				initAudioInputStream((URL) this.m_dataSource);
			else if ((this.m_dataSource instanceof File))
				initAudioInputStream((File) this.m_dataSource);
			else if ((this.m_dataSource instanceof InputStream))
				initAudioInputStream((InputStream) this.m_dataSource);
			createLine();
			Object localObject1 = null;
			if ((this.m_audioFileFormat instanceof AudioFileFormat))
			{
				localObject1 = ((AudioFileFormat) this.m_audioFileFormat).properties();
				localObject1 = deepCopy((Map) localObject1);
			}
			else
			{
				localObject1 = new HashMap();
			}
			if (this.m_audioFileFormat.getByteLength() > 0)
				((Map) localObject1).put("audio.length.bytes", new Integer(this.m_audioFileFormat.getByteLength()));
			if (this.m_audioFileFormat.getFrameLength() > 0)
				((Map) localObject1).put("audio.length.frames", new Integer(this.m_audioFileFormat.getFrameLength()));
			if (this.m_audioFileFormat.getType() != null)
				((Map) localObject1).put("audio.type", this.m_audioFileFormat.getType().toString());
			AudioFormat localAudioFormat = this.m_audioFileFormat.getFormat();
			if (localAudioFormat.getFrameRate() > 0.0F)
				((Map) localObject1).put("audio.framerate.fps", new Float(localAudioFormat.getFrameRate()));
			if (localAudioFormat.getFrameSize() > 0)
				((Map) localObject1).put("audio.framesize.bytes", new Integer(localAudioFormat.getFrameSize()));
			if (localAudioFormat.getSampleRate() > 0.0F)
				((Map) localObject1).put("audio.samplerate.hz", new Float(localAudioFormat.getSampleRate()));
			if (localAudioFormat.getSampleSizeInBits() > 0)
				((Map) localObject1).put("audio.samplesize.bits", new Integer(localAudioFormat.getSampleSizeInBits()));
			if (localAudioFormat.getChannels() > 0)
				((Map) localObject1).put("audio.channels", new Integer(localAudioFormat.getChannels()));
			if ((localAudioFormat instanceof AudioFormat))
			{
				// localObject2 = ((AudioFormat) localAudioFormat).properties();
				// ((Map) localObject1).putAll((Map) localObject2);
			}
			((Map) localObject1).put("basicplayer.sourcedataline", this.m_line);
			Object localObject2 = this.m_listeners.iterator();
			while (((Iterator) localObject2).hasNext())
			{
				// BasicPlayerListener localBasicPlayerListener =
				// (BasicPlayerListener) ((Iterator) localObject2).next();
				// localBasicPlayerListener.opened(this.m_dataSource, (Map)
				// localObject1);
			}
			this.m_status = 3;
			notifyEvent(1, getEncodedStreamPosition(), -1.0D, null);
		} catch (LineUnavailableException localLineUnavailableException)
		{
			// throw new BasicPlayerException(localLineUnavailableException);
			localLineUnavailableException.printStackTrace();
		} catch (UnsupportedAudioFileException localUnsupportedAudioFileException)
		{
			// throw new
			// BasicPlayerException(localUnsupportedAudioFileException);
			localUnsupportedAudioFileException.printStackTrace();
		} catch (IOException localIOException)
		{
			// throw new BasicPlayerException(localIOException);
			localIOException.printStackTrace();
		}
	}

	protected void initAudioInputStream(File paramFile)
			throws UnsupportedAudioFileException, IOException
	{
		this.m_audioInputStream = AudioSystem.getAudioInputStream(paramFile);
		this.m_audioFileFormat = AudioSystem.getAudioFileFormat(paramFile);
	}

	protected void initAudioInputStream(URL paramURL)
			throws UnsupportedAudioFileException, IOException
	{
		this.m_audioInputStream = AudioSystem.getAudioInputStream(paramURL);
		this.m_audioFileFormat = AudioSystem.getAudioFileFormat(paramURL);
	}

	protected void initAudioInputStream(InputStream paramInputStream)
			throws UnsupportedAudioFileException, IOException
	{
		this.m_audioInputStream = AudioSystem.getAudioInputStream(paramInputStream);
		this.m_audioFileFormat = AudioSystem.getAudioFileFormat(paramInputStream);
	}

	protected void initLine()
			throws LineUnavailableException
	{
		if (this.m_line == null)
			createLine();
		if (!this.m_line.isOpen())
		{
			openLine();
		}
		else
		{
			AudioFormat localAudioFormat1 = this.m_line.getFormat();
			AudioFormat localAudioFormat2 = this.m_audioInputStream == null ? null : this.m_audioInputStream.getFormat();
			if (!localAudioFormat1.equals(localAudioFormat2))
			{
				this.m_line.close();
				openLine();
			}
		}
	}

	protected void createLine()
			throws LineUnavailableException
	{
		if (this.m_line == null)
		{
			AudioFormat localAudioFormat1 = this.m_audioInputStream.getFormat();
			int i = localAudioFormat1.getSampleSizeInBits();
			if (i <= 0)
				i = 16;
			if ((localAudioFormat1.getEncoding() == AudioFormat.Encoding.ULAW) || (localAudioFormat1.getEncoding() == AudioFormat.Encoding.ALAW))
				i = 16;
			if (i != 8)
				i = 16;
			AudioFormat localAudioFormat2 = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, localAudioFormat1.getSampleRate(), i, localAudioFormat1.getChannels(), localAudioFormat1.getChannels() * (i / 8), localAudioFormat1.getSampleRate(), false);
			this.m_encodedaudioInputStream = this.m_audioInputStream;
			try
			{
				this.encodedLength = this.m_encodedaudioInputStream.available();
			} catch (IOException localIOException)
			{
				localIOException.printStackTrace();
			}
			this.m_audioInputStream = AudioSystem.getAudioInputStream(localAudioFormat2, this.m_audioInputStream);
			AudioFormat localAudioFormat3 = this.m_audioInputStream.getFormat();
			DataLine.Info localInfo = new DataLine.Info(SourceDataLine.class, localAudioFormat3, -1);
			Mixer localMixer = getMixer(this.m_mixerName);
			if (localMixer != null)
			{
				this.m_line = ((SourceDataLine) localMixer.getLine(localInfo));
			}
			else
			{
				this.m_line = ((SourceDataLine) AudioSystem.getLine(localInfo));
				this.m_mixerName = null;
			}
		}
	}

	protected void openLine()
			throws LineUnavailableException
	{
		if (this.m_line != null)
		{
			AudioFormat localAudioFormat = this.m_audioInputStream.getFormat();
			int i = this.lineBufferSize;
			if (i <= 0)
				i = this.m_line.getBufferSize();
			this.m_lineCurrentBufferSize = i;
			this.m_line.open(localAudioFormat, i);
			Control[] arrayOfControl = this.m_line.getControls();

			if (this.m_line.isControlSupported(FloatControl.Type.MASTER_GAIN))
			{
				this.m_gainControl = ((FloatControl) this.m_line.getControl(FloatControl.Type.MASTER_GAIN));
			}
			if (this.m_line.isControlSupported(FloatControl.Type.PAN))
			{
				this.m_panControl = ((FloatControl) this.m_line.getControl(FloatControl.Type.PAN));
			}
		}
	}

	protected void stopPlayback()
	{
		if ((this.m_status == 0) || (this.m_status == 1))
		{
			if (this.m_line != null)
			{
				this.m_line.flush();
				this.m_line.stop();
			}
			this.m_status = 2;
			notifyEvent(3, getEncodedStreamPosition(), -1.0D, null);
			synchronized (this.m_audioInputStream)
			{
				closeStream();
			}
		}
	}

	protected void pausePlayback()
	{
		if ((this.m_line != null) && (this.m_status == 0))
		{
			this.m_line.flush();
			this.m_line.stop();
			this.m_status = 1;
			notifyEvent(4, getEncodedStreamPosition(), -1.0D, null);
		}
	}

	protected void resumePlayback()
	{
		if ((this.m_line != null) && (this.m_status == 1))
		{
			this.m_line.start();
			this.m_status = 0;
			notifyEvent(5, getEncodedStreamPosition(), -1.0D, null);
		}
	}

	protected void startPlayback()

	{
		if (this.m_status == 2)
			initAudioInputStream();
		if (this.m_status == 3)
		{
			if ((this.m_thread != null) && (this.m_thread.isAlive()))
			{
				int i = 0;
				while (this.m_status != 3)
					try
					{
						if (this.m_thread != null)
						{
							i++;
							Thread.sleep(1000L);
							if (i > 2)
								this.m_thread.interrupt();
						}
					} catch (InterruptedException localInterruptedException)
					{
						// throw new BasicPlayerException("Wait error",
						// localInterruptedException);
						localInterruptedException.printStackTrace();
					}
			}
			try
			{
				initLine();
			} catch (LineUnavailableException localLineUnavailableException)
			{
				// throw new BasicPlayerException("Cannot init line",
				// localLineUnavailableException);
				localLineUnavailableException.printStackTrace();
			}
			this.m_thread = new Thread(this, "BasicPlayer");
			this.m_thread.start();
			if (this.m_line != null)
			{
				this.m_line.start();
				this.m_status = 0;
				notifyEvent(2, getEncodedStreamPosition(), -1.0D, null);
			}
		}
	}

	public void run()
	{
		int i = 1;
		byte[] arrayOfByte1 = new byte[EXTERNAL_BUFFER_SIZE];
		synchronized (this.m_audioInputStream)
		{
			while ((i != -1) && (this.m_status != 2) && (this.m_status != 4) && (this.m_status != -1))
				if (this.m_status == 0)
				{
					try
					{
						i = this.m_audioInputStream.read(arrayOfByte1, 0, arrayOfByte1.length);
						if (i >= 0)
						{
							byte[] arrayOfByte2 = new byte[i];
							System.arraycopy(arrayOfByte1, 0, arrayOfByte2, 0, i);

							int j = this.m_line.write(arrayOfByte1, 0, i);
							int k = getEncodedStreamPosition();
							Iterator localIterator = this.m_listeners.iterator();
							// while (localIterator.hasNext())
							// {
							// BasicPlayerListener localBasicPlayerListener
							// = (BasicPlayerListener) localIterator.next();
							// if ((this.m_audioInputStream instanceof
							// PropertiesContainer))
							// {
							// Map localMap = ((PropertiesContainer)
							// this.m_audioInputStream).properties();
							// localBasicPlayerListener.progress(k,
							// this.m_line.getMicrosecondPosition(),
							// arrayOfByte2, localMap);
							// }
							// else
							// {
							// localBasicPlayerListener.progress(k,
							// this.m_line.getMicrosecondPosition(),
							// arrayOfByte2, this.empty_map);
							// }
							// }
						}
					} catch (IOException localIOException)
					{
						localIOException.printStackTrace();
						this.m_status = 2;
						notifyEvent(3, getEncodedStreamPosition(), -1.0D, null);
					}
					if (this.threadSleep > 0L)
						try
						{
							Thread.sleep(this.threadSleep);
						} catch (InterruptedException localInterruptedException1)
						{
							localInterruptedException1.printStackTrace();
						}
				}
				else
				{
					try
					{
						Thread.sleep(1000L);
					} catch (InterruptedException localInterruptedException2)
					{
						localInterruptedException2.printStackTrace();
					}
				}
			if (this.m_line != null)
			{
				this.m_line.drain();
				this.m_line.stop();
				this.m_line.close();
				this.m_line = null;
			}
			if (i == -1)
				notifyEvent(8, getEncodedStreamPosition(), -1.0D, null);
			closeStream();
		}
		this.m_status = 2;
		notifyEvent(3, getEncodedStreamPosition(), -1.0D, null);
	}

	protected long skipBytes(long paramLong)

	{
		long l1 = 0L;
		if ((this.m_dataSource instanceof File))
		{
			int i = this.m_status;
			this.m_status = 4;
			long l2 = 0L;
			try
			{
				synchronized (this.m_audioInputStream)
				{
					notifyEvent(6, getEncodedStreamPosition(), -1.0D, null);
					initAudioInputStream();
					if (this.m_audioInputStream != null)
						while (l1 < paramLong - SKIP_INACCURACY_SIZE)
						{
							l2 = this.m_audioInputStream.skip(paramLong - l1);
							if (l2 != 0L)
							{
								l1 += l2;
							}
						}
				}
				notifyEvent(7, getEncodedStreamPosition(), -1.0D, null);
				this.m_status = 3;
				if (i == 0)
				{
					startPlayback();
				}
				else if (i == 1)
				{
					startPlayback();
					pausePlayback();
				}
			} catch (IOException localIOException)
			{
				localIOException.printStackTrace();
			}
		}
		return l1;
	}

	protected void notifyEvent(int paramInt1, int paramInt2,
			double paramDouble, Object paramObject)
	{
		// BasicPlayerEventLauncher localBasicPlayerEventLauncher = new
		// BasicPlayerEventLauncher(paramInt1, paramInt2, paramDouble,
		// paramObject, new ArrayList(this.m_listeners), this);
		// localBasicPlayerEventLauncher.start();
	}

	protected int getEncodedStreamPosition()
	{
		int i = -1;
		if ((this.m_dataSource instanceof File))
			try
			{
				if (this.m_encodedaudioInputStream != null)
					i = this.encodedLength - this.m_encodedaudioInputStream.available();
			} catch (IOException localIOException)
			{
			}
		return i;
	}

	protected void closeStream()
	{
		try
		{
			if (this.m_audioInputStream != null)
			{
				this.m_audioInputStream.close();
			}
		} catch (IOException localIOException)
		{
			localIOException.printStackTrace();
		}
	}

	public boolean hasGainControl()
	{
		if ((this.m_gainControl == null) && (this.m_line != null) && (this.m_line.isControlSupported(FloatControl.Type.MASTER_GAIN)))
			this.m_gainControl = ((FloatControl) this.m_line.getControl(FloatControl.Type.MASTER_GAIN));
		return this.m_gainControl != null;
	}

	public float getGainValue()
	{
		if (hasGainControl())
			return this.m_gainControl.getValue();
		return 0.0F;
	}

	public float getMaximumGain()
	{
		if (hasGainControl())
			return this.m_gainControl.getMaximum();
		return 0.0F;
	}

	public float getMinimumGain()
	{
		if (hasGainControl())
			return this.m_gainControl.getMinimum();
		return 0.0F;
	}

	public boolean hasPanControl()
	{
		if ((this.m_panControl == null) && (this.m_line != null) && (this.m_line.isControlSupported(FloatControl.Type.PAN)))
			this.m_panControl = ((FloatControl) this.m_line.getControl(FloatControl.Type.PAN));
		return this.m_panControl != null;
	}

	public float getPrecision()
	{
		if (hasPanControl())
			return this.m_panControl.getPrecision();
		return 0.0F;
	}

	public float getPan()
	{
		if (hasPanControl())
			return this.m_panControl.getValue();
		return 0.0F;
	}

	protected Map deepCopy(Map paramMap)
	{
		HashMap localHashMap = new HashMap();
		if (paramMap != null)
		{
			Iterator localIterator = paramMap.keySet().iterator();
			while (localIterator.hasNext())
			{
				Object localObject1 = localIterator.next();
				Object localObject2 = paramMap.get(localObject1);
				localHashMap.put(localObject1, localObject2);
			}
		}
		return localHashMap;
	}

	public long seek(long paramLong)

	{
		return skipBytes(paramLong);
	}

	public void play()

	{
		startPlayback();
	}

	public void stop()

	{
		stopPlayback();
	}

	public void pause()

	{
		pausePlayback();
	}

	public void resume()

	{
		resumePlayback();
	}

	public void setPan(double paramDouble)

	{
		if (hasPanControl())
		{
			this.m_panControl.setValue((float) paramDouble);
			notifyEvent(9, getEncodedStreamPosition(), paramDouble, null);
		}
	}

	public void setGain(double paramDouble)

	{
		if (hasGainControl())
		{
			double d1 = getMinimumGain();
			double d2 = 0.5F * getMaximumGain() - getMinimumGain();
			double d3 = Math.log(10.0D) / 20.0D;
			double d4 = d1 + 1.0D / d3 * Math.log(1.0D + (Math.exp(d3 * d2) - 1.0D) * paramDouble);
			this.m_gainControl.setValue((float) d4);
			notifyEvent(10, getEncodedStreamPosition(), paramDouble, null);
		}
		else
		{
			// throw new BasicPlayerException("Gain control not supported");
		}
	}

	public List getMixers()
	{
		ArrayList localArrayList = new ArrayList();
		Mixer.Info[] arrayOfInfo = AudioSystem.getMixerInfo();
		if (arrayOfInfo != null)
			for (int i = 0; i < arrayOfInfo.length; i++)
			{
				Line.Info localInfo = new Line.Info(SourceDataLine.class);
				Mixer localMixer = AudioSystem.getMixer(arrayOfInfo[i]);
				if (localMixer.isLineSupported(localInfo))
					localArrayList.add(arrayOfInfo[i].getName());
			}
		return localArrayList;
	}

	public Mixer getMixer(String paramString)
	{
		Mixer localMixer = null;
		if (paramString != null)
		{
			Mixer.Info[] arrayOfInfo = AudioSystem.getMixerInfo();
			if (arrayOfInfo != null)
				for (int i = 0; i < arrayOfInfo.length; i++)
					if (arrayOfInfo[i].getName().equals(paramString))
					{
						localMixer = AudioSystem.getMixer(arrayOfInfo[i]);
						break;
					}
		}
		return localMixer;
	}

	public String getMixerName()
	{
		return this.m_mixerName;
	}

	public void setMixerName(String paramString)
	{
		this.m_mixerName = paramString;
	}
}
